
#include "linear_schedule.h"
#include "kern_ll.h"
#include <assert.h>
#include <pthread.h>
#include <sched.h>
#include <unistd.h>
 #include <sys/unistd.h>
  #include <signal.h>
#include <sys/time.h> 
#include <pthread.h> 
//#define DEBUG_SCHED 1
#ifdef DEBUG
	//#define DEBUG_SCHED 1
#endif

//#define ENABLE_STATISTICS 1

void scheduler()
{


}


/**
  * function to initialize the scheduler
  * @param comm_arb_id -- comm arbiter ID
  */
void init_scheduler(int comm_arb_id)
{
	// obtain the processor count if possible
	p_count = (int )sysconf(_SC_NPROCESSORS_ONLN);	
	// set task count
	total_task_count=0;
	int i=0;
	int mask = p_count;
	// create a list of channels, equal to the size of the number
	// of processing elements	
	channels = (PLINKEDLIST*)malloc(p_count*sizeof(PLINKEDLIST));
	for (; i < p_count; i++)
	{
		channels[i] = NULL;
	}
		
	// set the scheduler to normal mode
    scheduler_mode = NORMAL_MODE;
    // nullify the work queues and set the pthread attributes
	work_queue = working_queue = NULL;
	sched_id = 0;
	// initialize the wait and task wait mutexes
	pthread_mutex_init(&wait_mutex, NULL);
	pthread_mutex_init(&wait_tasks_mutex, NULL);
	
	// init the thread attributes.
	pthread_condattr_init(&wait_attr);
	pthread_cond_init(&wait_scheduler,&wait_attr);
	pthread_cond_init(&wait_tasks_complete_cond,&wait_attr);
	
	int ret;
	int newprio = 20;
	struct sched_param param;

	// create the attributes and parameters for the priority types
	// code copies from lnll.gov
	ret = pthread_attr_init (&medprio);

	
	ret = pthread_attr_getschedparam (&medprio, &param);

	
	param.sched_priority = newprio;

	
	ret = pthread_attr_setschedparam (&medprio, &param);

	newprio = 30;
	
	ret = pthread_attr_init (&highprio);

	
	ret = pthread_attr_getschedparam (&highprio, &param);

	
	param.sched_priority = newprio;

	/* setting the new scheduling param */
	ret = pthread_attr_setschedparam (&highprio, &param);

	
	// run_linear_scheduler will run at the initiating program's 
	// priority; this this is intended to do the least amount of processing,
	// then there is absolutely no reason to give it a higher timeslice
	pthread_create( &scheduler_thread, NULL, &run_linear_scheduler, NULL);
	// wait until scheduler has been assigned an id
	while(sched_id == 0);
	// the reason for assigning the following threads to the last channel
	// is that typically, most OS schedulers assign processes to the
	// the first channel, thus the last channel should be the least loaded
	//
	if (pthread_setaffinity_np(pthread_self(),sizeof(mask),&mask) != 0)
		{
			// set program affinity to the last channel	
		}
		mask=1;
		if (pthread_setaffinity_np(sched_id,sizeof(mask),&mask) != 0)
		{
			
				 // set affinity of scheduler to the last channel
		}
		
		while(comm_arb_id == 0); // wait for communication arbiter
		// to go live
		if (comm_arb_id != 0)
		{
			mask=p_count;
			// statically schedule comm arbiter to last proc, which will
			// be the most available processor
			if (pthread_setaffinity_np(comm_arb_id,sizeof(mask),&mask) != 0)
			{
				
				
					
			}
		}
		
		total_task_count = 0; // 3 will always be assigned to last processor
		// set SIGALARM to catch the timer
		signal(SIGALRM,catch_sched);
		sched_scheduler(); // init timer
		// scheduler should be started
}


/**
  * initializes the statistics engine.
  */
void init_statistics()
{
	#ifdef ENABLE_STATISTICS
		scheduler_mode = STATISTICS_MODE;
		
		int err;
	struct timespec t;
	if (sysconf(_POSIX_THREAD_CPUTIME)){
	  err = clock_gettime(CLOCK_THREAD_CPUTIME_ID,&t);
	}
		printf("thread has been running for %lu %lu %i\n",t.tv_sec,t.tv_nsec,err);
	#endif
}//init_statistics



/**
  * shuts down the scheduler task
  */
void shutdown_scheduler()
{
	/** shutdown the scheduler by going through the channels, then
	 * freeing them
	 */
	int i=0;
	for (; i < p_count; i++)
		{
			// free the channel
			if (channels[i] != NULL)
				free(channels);
		}
	free(channels);
}

/**
  * schedules teh scheduler
  */
void sched_scheduler()
{
	// set timer
	struct itimerval old, new;
	new.it_interval.tv_usec = 0;
	new.it_interval.tv_sec = 0;
	new.it_value.tv_usec = (long int) NANOSECONDS_SLEEP;
	new.it_value.tv_sec = (long int) SECONDS_SLEEP;
	setitimer (ITIMER_REAL, &new, &old);
		
}


/**
  * signal catcher
  */
void catch_sched (int sig)
{
	// catch timer
	linear_sched();
	// signal that the timer has been caught
	signal (sig, catch_sched);
	// re-initialize timer
	sched_scheduler();
}

/**
  * Create_new_task creates a new parent task
  */
PTASK create_new_task(int sub_task_cnt, PTHREADFN fp, void *arg)
{
	// allocate memory for a new task, and assert
	// that it isn't null
	PTASK new_task = malloc( sizeof(TASK) );
	assert(new_task != NULL); // ensure new_task isn't null
	new_task->sub_tasks = malloc(sizeof(TASK)*sub_task_cnt);
	assert( new_task->sub_tasks != NULL );
	new_task->sub_task_count = sub_task_cnt;
	new_task->task_at=0;
	new_task->fp = fp;
	new_task->threadId = 0;
	// place thread in waiting mode
	new_task->status = THREAD_WAITING;
	new_task->arg = arg;
	// thread is a task controller
	new_task->type = THREAD_CONTROL;
	new_task->parent_task = NULL;
		#ifdef DEBUG_SCHED
		printf(" -- create_new_task -- new_task is %i\n",new_task);
	#endif
	return new_task;
}

/**
  * destroy_task frees memory for a task
  */
void destroy_task(PTASK task)
{
	// do some accounting ???
	free(task);
}
/**
  * schedule_thread is the function which is used for all tasks
  * to perform thread scheduling
  * @param ptr -- function pointer
  * @return result of the executing FP
  */
void *schedule_thread(void *ptr)
{
	
	PTASK task = (PTASK)ptr;
	// obtain thread TID
	task->threadId = pthread_self();
	int retval = 0;
	// continue until stopped
	while(task->status != THREAD_STOPPED)
	{
		
		// exec the function pointer
		if (task->type == REPLICA)
		{
			#ifdef DEBUG_SCHED
				printf("we have a shitty replica\n");
			#endif
		}
		// should never happen; for debugging only
		assert(task->fp == NULL);

		// the return value is actually the result of the running
		// work element
		retval = (*(task->fp))((void*)task->arg);
		
		// yield immediately
		if (retval == THREAD_YIELD)
			sched_yield();
			
		#ifdef ENABLE_STATISTICS
			if (sysconf(_POSIX_THREAD_CPUTIME)){
			 	clock_gettime(CLOCK_THREAD_CPUTIME_ID,&task->thread_stats.timer);
				}
		#endif
		// if a controller and we are to stop
		if (task->type == THREAD_CONTROL && retval == THREAD_STOPPED)
		{
			// shutdown this task sub tasks, replicated tasks
			// and a parent task
			#ifdef ENABLE_STATISTICS
			if (sysconf(_POSIX_THREAD_CPUTIME)){
			 	clock_gettime(CLOCK_THREAD_CPUTIME_ID,&task->thread_stats.timer);
				}
			#endif
			task->status = THREAD_STOPPED;
			u_int64_t running_time = shutdown_tasks(task);
			running_time += shutdown_replicated_tasks_fp(task->fp);
			shutdown_parent_task(task);
			task->thread_stats.running_time = running_time;
			break;
		}
		
		
		
		
	}
	return 0;
}

/**
  * shutdown_parent_task shuts down the associated parent task
  * @param task -- task to shutdown.
  */

void shutdown_parent_task(PTASK task)
{
	// as long as the task if not null, we set the status
	// of the thread to stopped
	if ((PTASK)task->parent_task != NULL)
		((PTASK)task->parent_task)->status = THREAD_STOPPED;
}


/**
  * shutdown_replicated_tasks_fp shutts down all tasks associated 
  * with a given function pointer
  * @param fp -- function pionter
  * @result result of the shutdown
  */

u_int64_t shutdown_replicated_tasks_fp(PTHREADFN fp)
{
	// go through the list of all running tasks, and shut them down
	int i = p_count-1;
	u_int64_t running_time = 0;
	// traverse all channels
	for (; i >= 0; i-- )
	{
		PLINKEDLIST queue=NULL;
		PWORKQUEUE queue_entry = NULL;	
		
		queue = channels[i];
		PTASK task_ptr;
		PTASK replicated_task;
		// we get the queue from the channel. Next we will
		// iterate through the queue, extract the entry
		// which provides us the task pointer. 
		// This task pointer is our link to the task structure
		// in memory.
		while(queue != NULL)
		{
			
			queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
			task_ptr = queue_entry->task_ptr;
			
			
			queue = queue->nextPtr;
			// shutdown tasks
			if (task_ptr->fp == fp || (task_ptr->parent_task!= NULL && ((PTASK)task_ptr->parent_task)->fp == fp))
				running_time+=shutdown_tasks(task_ptr);
			
		}
	}
	return running_time;
}


/**
  * task_count returns the count of tasks within a given task
  * @param task -- incoming task
  */

unsigned int task_count(PTASK task)
{
	unsigned int count = 0;
	int i = 0;
	for (; i < task->task_at; i++)
	{
		if ( ((PTASK)task->sub_tasks)[i].sub_task_count > 0)
		{
			// there are tasks within this, meaning that we don't need
			// to run this task, but rather the tasks within this
			count +=task_count( &task->sub_tasks[i] );
		}
		else
			count += 1;
		
	}
	count++; // for the main count
	#ifdef DEBUG_SCHED
		printf("task_count is  is %i\n",count);
	#endif
	return count;
}


/**
  * remove_task removes a task from a given channel
  * @param task -- incoming task
  * @param channel -- channel from which we will remove the given task
  */
  
void remove_task(PTASK task, unsigned int channel)
{
	#ifdef DEBUG_SCHED
		printf("remove_task(PTASK task, unsigned int channel) -- remove_task(%i,%i)\n",task,channel);
	#endif
	PLINKEDLIST queue=NULL;
	PWORKQUEUE queue_entry = NULL;	
	
	queue = channels[channel];
	PTASK task_ptr;
	while(queue != NULL)
	{
		queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
		task_ptr = queue_entry->task_ptr;
		if (task == task_ptr)
		{
			
			KernUnlinkListEntry(&channels[channel],&(queue_entry->queue_list));
			free(queue_entry);
		}
		if (queue == NULL)
			break;
		if (queue->nextPtr == NULL)
			break;
		queue = queue->nextPtr;
	}
}


/**
  * shutdown_tasks shuts down a task and all sub tasks
  */

u_int64_t shutdown_tasks(PTASK task)
{
	#ifdef DEBUG_SCHED
		printf("shutdown_tasks(PTASK task) -- shutdown_tasks(%i)\n",task);
	#endif
	// second argument is the attribute list
	int i = 0;
	u_int64_t running_time = 0;
	for (; i < task->task_at; i++)
	{
		if ( ((PTASK)task->sub_tasks)[i].sub_task_count > 0)
		{
			// there are tasks within this, meaning that we don't need
			// to run this task, but rather the tasks within this
			running_time+=shutdown_tasks( &task->sub_tasks[i] );
		}
		
		((PTASK)task->sub_tasks)[i].status = THREAD_STOPPED;
		#ifdef ENABLE_STATISTICS
			
			
			running_time += ((PTASK)task->sub_tasks)[i].thread_stats.timer.tv_sec*1000000000UL + ((PTASK)task->sub_tasks)[i].thread_stats.timer.tv_nsec;
			printf("running time is %lu\n",running_time);
		#endif	
	}
	#ifdef ENABLE_STATISTICS
	
		running_time += task->thread_stats.timer.tv_sec*1000000000UL + task->thread_stats.timer.tv_nsec;
		printf("running time is %lu\n",running_time);
	#endif
	remove_task(task, task->channel);
	return (running_time/1000000000UL);
}


/**
  * schedule_tasks creates the threads for a given task and sub tasks
  */

void schedule_tasks(PTASK task, unsigned int type)
{
	#ifdef DEBUG_SCHED
		printf(" --+ schedule_tasks(PTASK task, unsigned int type) -- schedule_tasks(%i,%i)\n",task,type);
	#endif
	// second argument is the attribute list
	int i = 0;
	
	for (; i < task->task_at; i++)
	{
		if ( ((PTASK)task->sub_tasks)[i].sub_task_count > 0)
		{
			//printf("we have sub tasks\n");
			// there are tasks within this, meaning that we don't need
			// to run this task, but rather the tasks within this
			schedule_tasks( &task->sub_tasks[i],type );
		}
		else
		{
			//printf("let's start this biatch\n");
			((PTASK)task->sub_tasks)[i].status = THREAD_RUNNING;
			#ifdef DEBUG_SCHED
			printf("calling schedule_thread with %i %i\n",&((PTASK)task->sub_tasks)[i],((PTASK)task->sub_tasks)[i]);
			#endif
			// create the thread
			pthread_create( &(((PTASK)task->sub_tasks)[i].thread), &highprio, &schedule_thread,&((PTASK)task->sub_tasks)[i] );	
			
		}
	}
	// set task as running
	task->status = THREAD_RUNNING;
	if (type == SINGLETON)
	{
		#ifdef DEBUG_SCHED
		printf("calling schedule_thread on singleton %i\n",task);
		#endif
		pthread_create( &(task->thread), &highprio, &schedule_thread,task );	
	}
	//pthread_cond_signal(&wait_tasks_complete_cond);
}



/**
  * get_task_working_cost returns the working cost of a task
  */

void get_task_working_cost(PTASK task,unsigned int *min, unsigned int *max)
{
	
	unsigned int cost = 0;
	int i = 0;
	for (; i < task->task_at; i++)
	{
		if ( ((PTASK)task->sub_tasks)[i].sub_task_count > 0)
		{
			// there are tasks within this, meaning that we don't need
			// to run this task, but rather the tasks within this
			get_task_working_cost(&task->sub_tasks[i],min,max );
		}
		else
		{
			cost = ((PTASK)task->sub_tasks)[i].run_cost+((PTASK)task->sub_tasks)[i].data_cost;
			if (cost < *min || *min == 0)
				*min=cost;
			if (cost > max || *max == 0)
				*max=cost;
		}
		
	}
	cost = task->run_cost+task->data_cost;
	if (cost < *min || *min == 0)
				*min=cost;
			if (cost > max || *max == 0)
				*max=cost;
}


/**
  * get_working_costs returns the working cost between a min and max
  */

void get_working_costs(unsigned int *min, unsigned int *max)
{
	PLINKEDLIST queue=NULL;
	PWORKQUEUE queue_entry = NULL;	
	queue = working_queue;
	PTASK task_ptr;
	while(queue != NULL)
	{
		queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
		task_ptr = queue_entry->task_ptr;
		
		get_task_working_cost(task_ptr,min,max);
		
		if (queue->nextPtr == NULL)
			break;
		queue = queue->nextPtr;
	}
}


/**
  * set_channel_task sets a task to a given channel
  */

void set_channel_task(PTASK task,unsigned int channel, unsigned int type)
{
	#ifdef DEBUG_SCHED
		printf(" --+ set_channel_task(PTASK task,unsigned int channel, unsigned int type) -- set_channel_task(%i,%i,%i)\n",task,channel,type);
	#endif
	unsigned int cost = 0;
	int i = 0;
	int processormask = channel+1;
	for (; i < task->task_at; i++)
	{
		if ( ((PTASK)task->sub_tasks)[i].sub_task_count > 0)
		{
			// there are tasks within this, meaning that we don't need
			// to run this task, but rather the tasks within this
			set_channel_task(&task->sub_tasks[i],channel,type );
		}
		else
		{
			if (((PTASK)task->sub_tasks)[i].threadId != 0)
			{
				#ifdef DEBUG_SCHED
				printf(" -- set_channel_task(..) -- assign to processor %i\n",processormask);
				#endif
				
				if (pthread_setaffinity_np(((PTASK)task->sub_tasks)[i].threadId,sizeof(processormask),&processormask) != 0)
					{
						// assign task affinity
						
							
					}
					((PTASK)task->sub_tasks)[i].channel = channel;
					
			
			}		
			
		}
		
	}
	if (task->threadId != 0)
	{
		
			if (type == SINGLETON)
			{
				#ifdef DEBUG_SCHED
				printf(" -- set_channel_task(..) -- (SINGLETON) assign to processor %i\n",processormask);
				#endif
				if (pthread_setaffinity_np(task->threadId,sizeof(processormask),&processormask) != 0)
				{
				//	printf("oh %i\n",processormask);
					
						
				
				}
			}
			
			PWORKQUEUE new_queue_entry = malloc(sizeof(WORKQUEUE));
			new_queue_entry->task_ptr = task;
			task->channel = channel;
			KernAddListEntry(&(channels[channel]),&(new_queue_entry->queue_list));
		//}
	}
	
}


/**
  * fill_channel replicates task until a channel is full
  */

void fill_channel(unsigned int channel,int *channel_count, unsigned int channel_size)
{
	#ifdef DEBUG_SCHED
		printf(" -- fill_channel(PTASK task,unsigned int *channel_count, unsigned int channel_size) -- fill_channel(%i,%i,%i)\n",channel,*channel_count,channel_size);
	#endif
	// cost_per_channel has changed to number of items per channel
	PLINKEDLIST queue=NULL;
	unsigned int channels = 0;
	PWORKQUEUE queue_entry = NULL;	
	queue = working_queue;
	PTASK task_ptr;
	
	PTASK replicated_task=NULL;
	
	while(queue != NULL)
	{
		queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
		task_ptr = queue_entry->task_ptr;
		#ifdef DEBUG_SCHED
			printf(" -- fill_channel(...) -- calling replicate_task\n");
		#endif
		replicated_task = replicate_task(task_ptr,NULL,NULL);
		// create thread, assert that replicated task isn't null
		assert(replicated_task != NULL);

		
		schedule_tasks( replicated_task, REPLICA );
		
		// set the affinity for that task and sub tasks
		set_channel_task(replicated_task,channel, REPLICA);
		
		// increase channel_count
		*channel_count+=task_count( replicated_task )-1;
		if (*channel_count >= channel_size)
		{
			#ifdef DEBUG_SCHED
				printf(" -- fill_channel(...) -- channel_count>=channel_size -- %i>=%i\n",*channel_count,channel_size);
			#endif

			break;
		}
		
		if (queue->nextPtr == NULL)
		{
			#ifdef DEBUG_SCHED
				printf(" -- fill_channel(...) -- nextPtr is NULL\n");
			#endif
			queue = working_queue;
			continue;
		}
		queue = queue->nextPtr;
	}
}


/**
  * shutdown_replicated_tasks shuts down replicated tasks
  */

void shutdown_replicated_tasks()
{
	#ifdef DEBUG_SCHED
		printf(" -- shutdown_replicated_tasks()\n");
	#endif
	// go through the list of all running tasks, and shut them down
	int i = p_count-1;
	for (; i >= 0; i-- )
	{
		PLINKEDLIST queue=NULL;
		PWORKQUEUE queue_entry = NULL;	
	
		queue = channels[i];
		PTASK task_ptr;
		PTASK replicated_task;
	
		while(queue != NULL)
		{
			queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
			task_ptr = queue_entry->task_ptr;
			
			
			queue = queue->nextPtr;
			shutdown_tasks(task_ptr);
			
		}
	}
}


/**
  * channelize_tasks channelizes tasks
  */

void channelize_tasks()
{
	#ifdef DEBUG_SCHED
		printf(" -- channelize_tasks()\n");
	#endif
	// shutdown all already replicated tasks
	shutdown_replicated_tasks();
	// computer tasks per 
	unsigned int tasks_per_channel = (int)((total_task_count/p_count + 1)*4); // round up
	printf("creating %i tasks\n",tasks_per_channel);
	// these are the number of channels that we now have with the number
	// of tasks that are running

	int channel=p_count-1;
	int i =0;
	int channel_count = 0;
	#ifdef DEBUG_SCHED
		printf(" -- tasks_per_channel is %i; channel_size is %i; total_task_count is %i\n", tasks_per_channel,p_count,total_task_count);
	#endif
	// first, we create one of each in the working queue and assign it to a task
	PLINKEDLIST queue=NULL;
	PWORKQUEUE queue_entry = NULL;	
	queue = working_queue;
	PTASK task_ptr;
	PTASK replicated_task;
	#ifdef DEBUG_SCHED
		printf("replicating first round of tasks\n");
	#endif

	// first traverse working_queue and replicate all tasks
	// after this process is complete, we will fill subsequent channels	
	while(queue != NULL)
	{
		queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
		task_ptr = queue_entry->task_ptr;
		//printf("replicating task\n");
		#ifdef DEBUG_SCHED
			printf(" -- channelize_tasks(...) -- calling replicate_task\n");
		#endif
		replicated_task = replicate_task(task_ptr,NULL,NULL);
		// create thread
		assert(replicated_task != NULL);
		#ifdef DEBUG_SCHED
		printf("replicated task is %i, child is %i\n",replicated_task,((PTASK)replicated_task->sub_tasks)[i]);
		#endif
		// schedule task as a SINGLETON
		schedule_tasks( replicated_task , SINGLETON);
		
		// set the affinity for that task and sub tasks
		set_channel_task(replicated_task,channel, SINGLETON);
		
		channel_count+=task_count( replicated_task );
		if (channel_count >= tasks_per_channel)
		{
			#ifdef DEBUG_SCHED
				printf(" -- channelize_tasks(...) -- channel_count>=channel_size -- %i %i %i\n",channel,channel_count,tasks_per_channel);
			#endif
			channel--;
			channel_count=0;
		}
		
		if (queue->nextPtr == NULL)
		{
			#ifdef DEBUG_SCHED
				printf(" -- channelize_tasks(...) -- nextPtr is NULL\n");
			#endif
			break;
		}
		queue = queue->nextPtr;
	}
	// fill each subsequent channel
	for (i=channel; i >= 0; i-- )
	{
		channel_count=0;
		fill_channel(i,&channel_count,tasks_per_channel);
	}
	
	
}


/**
  * run_linear_scheduler is the scheduler
  */


void *run_linear_scheduler(void)
{
	#ifdef DEBUG_SCHED
		printf(" --+ run_linear_scheduler()\n");
	#endif
	sched_id  = pthread_self();
	unsigned force_change = 0;
	PTASK task_ptr=NULL, task_ptr_b=NULL;;
	PLINKEDLIST queue=NULL;
	PLINKEDLIST wqueue=NULL;
	PWORKQUEUE queue_entry = NULL;
	PWORKQUEUE wqueue_entry = NULL;
	while(1)
	{
		pthread_cond_wait(&wait_scheduler,&wait_mutex);
		
		#ifdef DEBUG_SCHED
		printf(" --+ run_linear_scheduler()\n");
	#endif
		queue = work_queue;
		
		if (work_queue != NULL)
		{
			#ifdef DEBUG_SCHED
			printf(" -- run_linear_scheduler() -- work_queue isn't empty\n");
			#endif
			force_change=1;
		}
		// add new elements to the working queue
		while(queue != NULL)
		{
			#ifdef DEBUG_SCHED
			printf(" -- run_linear_scheduler() -- queue != NULL\n");
			#endif
			queue_entry = LIST_ENTRY_PTR(queue,WORKQUEUE,queue_list);
			task_ptr = queue_entry->task_ptr;
			total_task_count += task_count( task_ptr );
			//printf("total task count is %i\n",total_task_count);
			//schedule_tasks( task_ptr );
			//KernUnlinkListEntry(&work_queue,&(queue_entry->queue_list));
				
			//KernMoveListPtr(&work_queue,&working_queue,&(queue_entry->queue_list),&(queue_entry->queue_list));
			
			if (working_queue == NULL)
			{
				
					#ifdef DEBUG_SCHED
					printf(" -- run_linear_scheduler() -- working\n");
					#endif
				KernMoveListPtr(&work_queue,&working_queue,&(queue_entry->queue_list),&(queue_entry->queue_list));
					
				queue = work_queue;
				continue;
			}
			else
			{
				
				wqueue = working_queue;
					// add new elements to the working queue
					// ordering by task cost
					while(wqueue != NULL)
					{
						#ifdef DEBUG_SCHED
								printf(" -- run_linear_scheduler() -- check-queue\n",task_ptr_b);
							#endif
						wqueue_entry = LIST_ENTRY_PTR(wqueue,WORKQUEUE,queue_list);
						task_ptr_b = wqueue_entry->task_ptr;
						// order the working queue based upon costs
						if ((task_ptr->total_data_cost+task_ptr->total_run_cost) 
							<= task_ptr_b->total_data_cost+task_ptr_b->total_run_cost)
						{
							#ifdef DEBUG_SCHED
								printf(" -- run_linear_scheduler() -- task_ptr <= task_ptr_b ; %u %i\n",task_ptr,task_ptr_b);
							#endif
							if (wqueue->nextPtr == NULL)
							{
								#ifdef DEBUG_SCHED
								printf(" -- run_linear_scheduler() -- wqueue->nextPtr == NULL\n");
								#endif
								KernUnlinkListEntry(&work_queue,&(queue_entry->queue_list) );
								queue_entry->queue_list.nextPtr = NULL;
  								queue_entry->queue_list.previousPtr=NULL;  
								KernAddListEntryAfter(&working_queue,wqueue,&(queue_entry->queue_list));
								break;
							}
							else
								{
									#ifdef DEBUG_SCHED
								printf(" -- run_linear_scheduler() -- next! %i %i %i\n", wqueue, wqueue->nextPtr,working_queue);
							#endif
								}
								
							wqueue = wqueue->nextPtr;
						}
						else
						{
							#ifdef DEBUG_SCHED
								printf(" -- run_linear_scheduler() -- simple move\n");
								#endif
							KernMoveListPtr(&work_queue,&working_queue,&(queue_entry->queue_list),&(queue_entry->queue_list));
							//KernAddListEntry(&queue,&(new_queue_entry->queue_list));
							break;
						}
					}
					printf("out of the while loop\r\n");
			}		
			
			queue = work_queue;
			
			
			if (queue == NULL)
				break;
			if (queue->nextPtr == NULL)
				break;
			queue = queue->nextPtr;
		}
		if (force_change == 1)
		{
			channelize_tasks();
			force_change=0;
		}
	//	pthread_cond_signal(&wait_tasks_complete_cond);
		
	}
	
	// create thread for scheduler
	// current thread is the scheduler
}


void linear_sched()
{
	// calls the scheduler
	pthread_cond_signal(&wait_scheduler);
	
	
}


/**
  * runs a task
  */

void run_task(PTASK task_to_run)
{
	#ifdef DEBUG_SCHED
		printf(" --+ run_task(PTASK) -- run-task(%i)\n",task_to_run);
	#endif
	
	PWORKQUEUE new_queue_entry = malloc(sizeof(WORKQUEUE));
	PWORKQUEUE queue_entry = NULL;
	PWORKQUEUE queue;
	new_queue_entry->task_ptr = task_to_run;
	
	// signal scheduler that we gave it something
	
	unsigned int run=0,data=0;
	calculate_cost(task_to_run,&run,&data);
	
	
	#ifdef ENABLE_STATISTICS
	if (sysconf(_POSIX_THREAD_CPUTIME)){
	  clock_gettime(CLOCK_THREAD_CPUTIME_ID,&(task_to_run->thread_stats.timer));
	}
	
	#endif
	
	// modifcations
	//task_cost = run+data;
	pthread_mutex_lock( &wait_mutex ); //wait_mutex
	queue = work_queue;
	KernAddListEntry(&work_queue,&(new_queue_entry->queue_list));
	pthread_mutex_unlock( &wait_mutex ); //wait_mutex
	pthread_cond_signal(&wait_scheduler);
	
		
}


/**
  * wait_tasks_complete causes the calling thread to wait
  */

void wait_tasks_complete()
{
	pthread_cond_wait(&wait_tasks_complete_cond,&wait_tasks_mutex);
	PLINKEDLIST wqueue=NULL;
	PTASK task_ptr=NULL;
	PWORKQUEUE wqueue_entry = NULL;
//	threadId
	wqueue = working_queue;
					// add new elements to the working queue
					// ordering by task cost
		while(wqueue != NULL)
		{
			#ifdef DEBUG_SCHED
					
			#endif
			wqueue_entry = LIST_ENTRY_PTR(wqueue,WORKQUEUE,queue_list);
			task_ptr = wqueue_entry->task_ptr;
			
			if (task_ptr->status != THREAD_STOPPED)
			{
				pthread_cond_wait(&wait_tasks_complete_cond,&wait_tasks_mutex);
				wqueue = working_queue;
				continue;
			}
			
			if (wqueue->nextPtr == NULL)
				break;
			
			
			wqueue = wqueue->nextPtr;
			
		}
}


/**
  * wait_task yields calling thread until a specified task has been
  * stopped
  */

//1 000 000 000
u_int64_t delta_timer(struct timespec *t, struct timespec *t2)
{
	u_int64_t delta = t2->tv_sec*1000000000UL;
	delta += t2->tv_nsec;
	delta -= ( (t->tv_sec*1000000000UL) + t->tv_nsec );
	return delta;
}

void wait_task(PTASK task_to_wait)
{
	if (task_to_wait->type == THREAD_CONTROL)
	{
		while(task_to_wait->status != THREAD_STOPPED)
		{
			//printf("*");
			sched_yield();
			
		}
		
	}
	#ifdef ENABLE_STATISTICS
	
	struct timespec newtime;
	if (sysconf(_POSIX_THREAD_CPUTIME)){
	 clock_gettime(CLOCK_THREAD_CPUTIME_ID,&newtime);
	}
	
	printf("task has been running for %lu nanoseconds\n",delta_timer(&task_to_wait->thread_stats.timer,&newtime));
	
	printf("Task has been distributed for %lu nanoseconds ( %lu seconds )\n",task_to_wait->thread_stats.running_time,task_to_wait->thread_stats.running_time/1000000000UL);
	#endif
}


/**
  * create_worker creates a sub task for a given task using 
  * the specified arguments
  */

void create_worker(PTASK parent_task,PTHREADFN fn,void *arg, unsigned int run_cost, unsigned int data_size)
{
	if (parent_task->task_at >= parent_task->sub_task_count )
		return;
		
	PTASK new_task = &(((PTASK)parent_task->sub_tasks)[ parent_task->task_at ]);
	new_task->fp = fn;
	new_task->type = THREAD_WORKER;
	new_task->run_cost=run_cost;
	new_task->data_cost=data_size;
	new_task->arg = arg;
	new_task->task_at = 0;
	new_task->parent_task = NULL;
	new_task->threadId = 0;
	new_task->status = THREAD_WAITING;
	new_task->sub_task_count = 0;
	new_task->sub_tasks = NULL;
	
	parent_task->task_at++;
	#ifdef DEBUG_SCHED
		printf(" -- create_worker -- new_task is %i\n",new_task);
	#endif
	// pthread_create our thread and 
}



/**
  * replicate task replicates a task and all sub tasks
  * @param parent_task -- parent task to replicate
  * @param newtask -- newly allocated memory for a task ( can be null)
  * and we'll go through the process of allocating memory
  * @param sub_task  -- subordinate task
  */

PTASK replicate_task (PTASK parent_task, PTASK newtask, PTASK sub_task)
{
	#ifdef DEBUG_SCHED
		printf("replicate_task (PTASK parent_task, PTASK newtask, PTASK sub_task) -- replicate_task(%i,%i,%i)\n",parent_task,newtask,sub_task);
	#endif
	
	// can only replicate a parent task
	unsigned int i=0;
	
	if (newtask == NULL && sub_task == NULL)
	{
		PTASK new_task = (PTASK)malloc( sizeof(TASK) );
		#ifdef DEBUG_SCHED
			printf("created new task %i\n",new_task);
		#endif
		// essentially we just copy all the task data
		new_task->sub_tasks = malloc(sizeof(TASK)*(parent_task->sub_task_count));
		new_task->sub_task_count = parent_task->sub_task_count;
		new_task->task_at= parent_task->task_at;
		new_task->fp = parent_task->fp;
		new_task->threadId = 0;
		new_task->parent_task = parent_task;
		// identify that the new task is a waiting task and isn't running
		new_task->status = THREAD_WAITING;
		new_task->arg = parent_task->arg;
		// also identify that this is a replica.
		new_task->type = THREAD_CONTROL_REPLICA;
		for (; i < new_task->sub_task_count; i++)
		{
			#ifdef DEBUG_SCHED
				printf("sub task ptr is %i\n",((PTASK)parent_task->sub_tasks)[i].fp);
			#endif
			replicate_task(new_task,&(new_task->sub_tasks[i]),&(parent_task->sub_tasks[i]));
		}
		#ifdef DEBUG_SCHED
			printf("returning new_task\n");
		#endif
		return new_task;
	}
	else
	{
		#ifdef DEBUG_SCHED
		if (sub_task->fp == NULL)
		{
			// is this really a complete failure point?
			printf(" SUB TASK IS NULL!\n");
			exit(1);
		}
		else
			printf("sub task ptr is %i\n",sub_task->fp);
		#endif
		newtask->fp = sub_task->fp;
		#ifdef DEBUG_SCHED
			printf("created new sub task %i\n",newtask);
			printf("newtask ptr is %i\n",newtask->fp);
		#endif
		newtask->type = THREAD_WORKER_REPLICA;
		newtask->run_cost=sub_task->run_cost;
		newtask->data_cost=sub_task->data_cost;
		newtask->arg = sub_task->arg;
		newtask->task_at = sub_task->task_at;
		newtask->parent_task = parent_task;
		newtask->threadId = 0;
		newtask->status = THREAD_WAITING;
		newtask->sub_task_count = sub_task->sub_task_count;
		#ifdef DEBUG_SCHED
		printf("setting sub task count to %i\n",newtask->sub_task_count );
		#endif
		newtask->sub_tasks = malloc(sizeof(TASK)*(newtask->sub_task_count));
		
		for (; i < newtask->sub_task_count; i++)
		{
			replicate_task(newtask,&(newtask->sub_tasks[i]),&(sub_task->sub_tasks[i]));
		}
		
		
	}
	return NULL;
}

/**
  * defunt method. no longer used
  */
void calculate_cost(PTASK task,unsigned int *run, unsigned int *data)
{
	#ifdef DEBUG_SCHED
	printf("calculate_cost\n");
	#endif
	unsigned int cost = 0;
	int i = 0;
	*run = 0;
	*data = 0;
	for (; i < task->task_at; i++)
	{
		if ( ((PTASK)task->sub_tasks)[i].sub_task_count > 0)
		{
			// there are tasks within this, meaning that we don't need
			// to run this task, but rather the tasks within this
			get_task_working_cost(&task->sub_tasks[i],run,data );
		}
		else
		{
			*run+= ((PTASK)task->sub_tasks)[i].run_cost;
			*data+=((PTASK)task->sub_tasks)[i].data_cost;
					}
		
	}
	task->total_run_cost = *run;
	task->total_data_cost = *data;
}
